///////////// vars ///////////////////
#define       DCF77PIN           3
#define       DCF77BLINKPIN      4
#define       DS1307_I2C_ADDRESS 0x68  // This is the I2C address
int           DS1307_V        =  50;
unsigned long dcf_last_time   =  0;

#define       DCF_split_millis   140
#define       DCF_sync_millis    1200
#define       DCF_MAX_JITTER     10
#define       DCF_SHORT_SIGNAL   50

/**
 * DCF time format struct
 */
struct DCF77Buffer {
unsigned long long prefix	:
  21;
unsigned long long Min	:
  7;	// minutes
unsigned long long P1		:
  1;	// parity minutes
unsigned long long Hour	:
  6;	// hours
unsigned long long P2		:
  1;	// parity hours
unsigned long long Day	:
  6;	// day
unsigned long long Weekday	:
  3;	// day of week
unsigned long long Month	:
  5;	// month
unsigned long long Year	:
  8;	// year (5 -> 2005)
unsigned long long P3		:
  1;	// parity
};

struct {
unsigned char parity_flag	:
  1;
unsigned char parity_min	:
  1;
unsigned char parity_hour	:
  1;
unsigned char parity_date	:
  1;
} 
flags;

volatile unsigned char DCFSignalState = 0;
unsigned char previousSignalState;
int previousFlankTime;


unsigned long long dcf_rx_buffer;
unsigned char previousSecond;
/**
 * Clock variables
 */
int bufferPosition;
/**
 * time vars: the time is stored here! -> geändert auf int ... testen .. war char
 */
volatile unsigned int ss;
volatile unsigned int mm;
volatile unsigned int hh;
volatile unsigned int day;
volatile unsigned int mon;
volatile unsigned int year;
volatile unsigned int dayOfWeek;
///////////// vars ///////////////////
boolean clock_changed(){
  if(disp_zeile_bak[4]==ss){
    return false;
  };
  disp_zeile_bak[4]=ss;
  return true;
};

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

/**
 * Initialize the DCF77 routines: initialize the variables,
 * configure the interrupt behaviour.
 */
void DCF77Init() {
  // Spannungsversorgung Modul
  pinMode(DS1307_V, OUTPUT);
  digitalWrite(DS1307_V, HIGH);

  // DCF77 Vars
  previousSignalState=0;
  previousFlankTime=0;
  bufferPosition=0;
  dcf_rx_buffer=0;

  // Init Uhrzeit lesen
  Wire.begin();
  // modul lesen
  Wire.beginTransmission(DS1307_I2C_ADDRESS);   // Open I2C line in write mode
  Wire.send(0x00);                              // Set the register pointer to (0x00)
  Wire.endTransmission();                       // End Write Transmission 
  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);      // Open the I2C line in send mode
  ss         = bcdToDec(Wire.receive() & 0x7f); // Read seven bytes of data
  mm         = bcdToDec(Wire.receive());
  hh         = bcdToDec(Wire.receive() & 0x3f);  
  dayOfWeek  = bcdToDec(Wire.receive());
  day        = bcdToDec(Wire.receive());
  mon        = bcdToDec(Wire.receive());
  year       = bcdToDec(Wire.receive());  
  time_valid=301;
  if(clock_debug){
    Serial.print("Die aktuelle Uhrzeit: ");
    Serial.print(hh);
    Serial.print(":");
    Serial.print(mm);
    Serial.print(" ");
    Serial.print(day);
    Serial.print(".");
    Serial.print(mon);
    Serial.print(".");
    Serial.print(year);
    Serial.print("\n");
  };
  // hier vom lustigen modul lesen ende

  // Pins und handler setzen
  pinMode(DCF77BLINKPIN, OUTPUT);
  pinMode(DCF77PIN, INPUT);
  attachInterrupt(1, int0handler, CHANGE); // interrupt handler für signalwechsel
}


void clock_store(){ 
  if(clock_debug){
    Serial.println("== Writing to Modul ==");
  };
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0x00);        
  Wire.send(decToBcd(ss));
  Wire.send(decToBcd(mm));
  Wire.send(decToBcd(hh));      
  Wire.send(decToBcd(dayOfWeek));
  Wire.send(decToBcd(day));
  Wire.send(decToBcd(mon));
  Wire.send(decToBcd(year));
  Wire.endTransmission();                    // End write mode
  if(clock_debug){
    Serial.println("== Done ==");
  };
};

/**
 * Append a signal to the dcf_rx_buffer. Argument can be 1 or 0. An internal
 * counter shifts the writing position within the buffer. If position > 59,
 * a new minute begins -> time to call finalizeBuffer().
 */
void appendSignal(unsigned char signal) {
  dcf_rx_buffer = dcf_rx_buffer | ((unsigned long long) signal << bufferPosition);
  // Update the parity bits. First: Reset when minute, hour or date starts.
  if (bufferPosition ==  21 || bufferPosition ==  29 || bufferPosition ==  36) {
    flags.parity_flag = 0;
  }
  // save the parity when the corresponding segment ends
  if (bufferPosition ==  28) {
    flags.parity_min = flags.parity_flag;
  };
  if (bufferPosition ==  35) {
    flags.parity_hour = flags.parity_flag;
  };
  if (bufferPosition ==  58) {
    flags.parity_date = flags.parity_flag;
  };
  // When we received a 1, toggle the parity flag
  if (signal == 1) {
    flags.parity_flag = flags.parity_flag ^ 1;
  }
  bufferPosition++;
  if(clock_debug){
    byte temp=signal+'0';
    Serial.print(temp,BYTE);
    Serial.print(" appended at bufferposition ");
    Serial.print(bufferPosition);
    Serial.print("\n");
  };
  if (bufferPosition > 59) {
    finalizeBuffer();
  }
}

/**
 * Evaluates the information stored in the buffer. This is where the DCF77
 * signal is decoded and the internal clock is updated.
 */
void finalizeBuffer(void) {      
  if(clock_debug){
    Serial.println(" -> Sync, reseting bufferposition");
  };

  if (bufferPosition >= 59) {
    if(clock_debug){
      Serial.println("Finalizing Buffer");
    };
    struct DCF77Buffer *rx_buffer;
    rx_buffer = (struct DCF77Buffer *)(unsigned long long)&dcf_rx_buffer;

    if (flags.parity_min == rx_buffer->P1  &&  flags.parity_hour == rx_buffer->P2  && flags.parity_date == rx_buffer->P3)
    {
      //convert the received bits from BCD
      int new_mm = rx_buffer->Min-((rx_buffer->Min/16)*6);
      int new_hh = rx_buffer->Hour-((rx_buffer->Hour/16)*6);
      int new_day= rx_buffer->Day-((rx_buffer->Day/16)*6);
      int new_mon= rx_buffer->Month-((rx_buffer->Month/16)*6);
      int new_year= rx_buffer->Year-((rx_buffer->Year/16)*6);

      int time_differ = (hh*60+mm+day+mon)-(new_hh*60+new_mm+new_day+new_mon);
      if(time_differ<0)  {  time_differ=(-1)*time_differ;  }; //abs macht fehler
 
      if(clock_debug){
        Serial.print("Die alte Uhrzeit war ");
        Serial.print(hh);
        Serial.print(":");
        Serial.print(mm);
        Serial.print(" ");
        Serial.print(day);
        Serial.print(".");
        Serial.print(mon);
        Serial.print(".");
        Serial.print(year);
        Serial.print("\n");
        
        Serial.println("Die neue ist jetzt ");
        Serial.print(new_hh);
        Serial.print(":");
        Serial.print(new_mm);
        Serial.print(" ");
        Serial.print(new_day);
        Serial.print(".");
        Serial.print(new_mon);
        Serial.print(".");
        Serial.print(new_year);
        Serial.print("\n");
        Serial.print("Die Differenz ist in Minuten: ");
        Serial.print(time_differ);
        Serial.print(" und das bedeutet ");
      };

      if ((time_differ<10 || (time_differ>50 && time_differ<70)) && (new_mm<60 && new_hh<24)) { // entweder nur 10 min Zeit differenz oder zwischen 50 und 70 ( +/- 10 min und Zeitumstellung )
        mm = new_mm;
        hh = new_hh;
        day= new_day;
        mon= new_mon;
        year= new_year;
        // hier ins modul schreiben, klappt nicht daher in der loop() 
        if(clock_debug){
          Serial.print("ich mache also ein RTC update\n Die Aktuelle Zeit:");
          Serial.print(hh); 
          Serial.print(":"); 
          Serial.print(mm); 
          Serial.print(" "); 
          Serial.print(day); 
          Serial.print("/"); 
          Serial.print(mon); 
          Serial.print("/"); 
          Serial.print(year);
          Serial.print("\n");
        };
        if(menu_state==861){
//          GLCD.GotoXY(55,25);
//          GLCD.Puts("done");
//          delay(500);
          menu_state=11;
          menu_disp();
        };
        time_valid=1;
      } 
      else {
        if(clock_debug){
          Serial.print("ich mache also KEIN RTC update\n");
        };
      };
    };
  };
  // reset stuff
  // ss = 0;
  bufferPosition = 0;
  dcf_rx_buffer=0;
}

int clock_getdate(){
  return day+mon*100;
};

/**
 * Evaluates the signal as it is received. Decides whether we received
 * a "1" or a "0" based on the
 */
void scanSignal(void){
  //Serial.println("Scan Signal");
  digitalWrite(DCF77BLINKPIN, DCFSignalState);
  if (DCFSignalState == 1) { // wir haben einen high pegel gelesen
    //GLCD.SetDot(55,10,BLACK);
    int thisFlankTime=millis();
    if (thisFlankTime - previousFlankTime > DCF_sync_millis) { // 2 flanken die weiter entfernt sind als sync -> neue minute
      finalizeBuffer();
    }
    previousFlankTime=thisFlankTime;
  }
  else {
    //GLCD.SetDot(55,10,WHITE);
    /* or a falling flank */
    int difference=millis() - previousFlankTime;
    // wenn das signal kürzer als 140 ms aber länger als 50 ms war => 0
    if (difference < DCF_split_millis && difference > DCF_SHORT_SIGNAL) {  
      appendSignal(0);  
    }
    // wenn das signal länger als 140 ms und länger als 50 ms war => 0
    else if (difference > DCF_split_millis && difference > DCF_SHORT_SIGNAL ) {  
      appendSignal(1);  
    }
  }
}

/**
 * Der hier passt auf den pin auf, und ändert einfach nur den zustand wenn eine änderung
 * außerhalb der JitterZeit vorliegt
 */
void int0handler() {
  if( speed_copy()==0 ) { //  && 
    //Serial.println("int0handler changed");
    int length = millis() - previousFlankTime;
    if (length < DCF_MAX_JITTER) {
      // ignore jitter
      return;
    }
    // check the value again - since it takes some time to activate the interrupt routine, we get a clear signal.
    DCFSignalState = !digitalRead(DCF77PIN);
    if (DCFSignalState != previousSignalState) {
      scanSignal();
      previousSignalState = DCFSignalState;  
    }
  }
}

void clock_inc(){  
  if(time_valid>0){ 
    ss++;
    if (ss==60) {
      ss=0;
      mm++;
      if (mm==60) {
        mm=0;
        hh++;
        if (hh==24)
          hh=0;
      }
    }  
    time_valid++; 
    if(time_valid>32700){
      time_valid=400;
    };  
  }; // 0==kein signal, >0 = sek seit letztem sync
};

void clock_copy(){
  char point;
  if(time_valid < 300){ 
    point='.';
  } // setze füür die ersten 5 min einen Punkt: exakte Zeit
  else { 
    point=' ';
  };
  sprintf(clock_out,"%02i:%02i:%02i%c",hh,mm,ss,point);
};

